package com.kaiwu.tc.factory;

import com.kaiwu.tc.annotations.Autowired;
import com.kaiwu.tc.annotations.Service;
import com.kaiwu.tc.annotations.Transactional;
import com.kaiwu.tc.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;

/**
 * 代理对象工厂：生成代理对象的
 */
@Service("proxyFactory")
public class ProxyFactory {
    @Autowired
    private TransactionManager transactionManager;

    public Object getProxy(Object obj){
        if (obj.getClass().getInterfaces() != null && obj.getClass().getInterfaces().length > 0){
            return this.getJdkProxy(obj);
        }else {
            return this.getCglibProxy(obj);
        }
    }

    /**
     * Jdk动态代理
     * @param obj  委托对象
     * @return   代理对象
     */
    private Object getJdkProxy(Object obj) {

        // 获取代理对象
        return  Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
                (proxy, method, args) -> enhanceInvoke(obj, method, args));

    }

    /**
     * 使用cglib动态代理生成代理对象
     * @param obj 委托对象
     * @return
     */
    private Object getCglibProxy(Object obj) {
        return  Enhancer.create(obj.getClass(), (MethodInterceptor) (o, method, objects, methodProxy) -> enhanceInvoke(obj, method, objects));
    }

    private Object enhanceInvoke(Object obj, Method method, Object[] objects) throws SQLException, IllegalAccessException, InvocationTargetException {
        Object result = null;
        try{
            if (haveTransactionAnno(obj, method)) {
                // 开启事务(关闭事务的自动提交)
                transactionManager.beginTransaction();
            }

            result = method.invoke(obj,objects);

            if (haveTransactionAnno(obj, method)) {
                // 提交事务
                transactionManager.commit();
            }
        }catch (Exception e) {
            e.printStackTrace();
            if (haveTransactionAnno(obj, method)) {
                // 回滚事务
                transactionManager.rollback();
            }
            // 抛出异常便于上层servlet捕获
            throw e;

        }
        return result;
    }

    private boolean haveTransactionAnno(Object obj, Method method){
        boolean classAnno = obj.getClass().isAnnotationPresent(Transactional.class);
        boolean methodAnno = false;
        Method[] methods = obj.getClass().getMethods();
        for (Method m: methods){
            if (m.getName().equals(method.getName())){
                methodAnno = m.isAnnotationPresent(Transactional.class);
                break;
            }
        }
        return classAnno || methodAnno;
    }

}
